home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / php / pear / MDB / pgsql.php < prev    next >
PHP Script  |  2004-03-24  |  53KB  |  1,455 lines

  1. <?php
  2. // +----------------------------------------------------------------------+
  3. // | PHP Version 4                                                        |
  4. // +----------------------------------------------------------------------+
  5. // | Copyright (c) 1998-2004 Manuel Lemos, Tomas V.V.Cox,                 |
  6. // | Stig. S. Bakken, Lukas Smith                                         |
  7. // | All rights reserved.                                                 |
  8. // +----------------------------------------------------------------------+
  9. // | MDB is a merge of PEAR DB and Metabases that provides a unified DB   |
  10. // | API as well as database abstraction for PHP applications.            |
  11. // | This LICENSE is in the BSD license style.                            |
  12. // |                                                                      |
  13. // | Redistribution and use in source and binary forms, with or without   |
  14. // | modification, are permitted provided that the following conditions   |
  15. // | are met:                                                             |
  16. // |                                                                      |
  17. // | Redistributions of source code must retain the above copyright       |
  18. // | notice, this list of conditions and the following disclaimer.        |
  19. // |                                                                      |
  20. // | Redistributions in binary form must reproduce the above copyright    |
  21. // | notice, this list of conditions and the following disclaimer in the  |
  22. // | documentation and/or other materials provided with the distribution. |
  23. // |                                                                      |
  24. // | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken,    |
  25. // | Lukas Smith nor the names of his contributors may be used to endorse |
  26. // | or promote products derived from this software without specific prior|
  27. // | written permission.                                                  |
  28. // |                                                                      |
  29. // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  |
  30. // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT    |
  31. // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS    |
  32. // | FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE      |
  33. // | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,          |
  34. // | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
  35. // | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
  36. // |  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED  |
  37. // | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT          |
  38. // | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
  39. // | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE          |
  40. // | POSSIBILITY OF SUCH DAMAGE.                                          |
  41. // +----------------------------------------------------------------------+
  42. // | Author: Paul Cooper <pgc@ucecom.com>                                 |
  43. // +----------------------------------------------------------------------+
  44. //
  45. // $Id: pgsql.php,v 1.62.4.14 2004/01/08 13:43:02 lsmith Exp $
  46.  
  47. require_once('MDB/Common.php');
  48.  
  49. /**
  50.  * MDB PostGreSQL driver
  51.  *
  52.  * Notes:
  53.  * - Creation of new databases is based on database template1.
  54.  *
  55.  * - The decimal type fields are emulated with integer fields.
  56.  *
  57.  * - PostgreSQL stores large objects in files managed by the server.
  58.  *   Tables with large object fields only store identifiers pointing to those
  59.  *   files. If you delete or update rows of those tables, the actual large
  60.  *   object files are not deleted from the server file system. Therefore you may
  61.  *   need to reclaim large object field space by deleting those files manually.
  62.  *
  63.  * @package MDB
  64.  * @category Database
  65.  * @author  Paul Cooper <pgc@ucecom.com>
  66.  */
  67.  
  68. class MDB_pgsql extends MDB_Common
  69. {
  70.     var $connection = 0;
  71.     var $connected_host;
  72.     var $connected_port;
  73.     var $selected_database = '';
  74.     var $opened_persistent = '';
  75.  
  76.     var $escape_quotes = "\\";
  77.     var $decimal_factor = 1.0;
  78.  
  79.     var $highest_fetched_row = array();
  80.     var $columns = array();
  81.  
  82.     // }}}
  83.     // {{{ constructor
  84.  
  85.     /**
  86.     * Constructor
  87.     */
  88.     function MDB_pgsql()
  89.     {
  90.         $this->MDB_Common();
  91.         $this->phptype = 'pgsql';
  92.         $this->dbsyntax = 'pgsql';
  93.  
  94.         $this->supported['Sequences'] = 1;
  95.         $this->supported['Indexes'] = 1;
  96.         $this->supported['SummaryFunctions'] = 1;
  97.         $this->supported['OrderByText'] = 1;
  98.         $this->supported['Transactions'] = 1;
  99.         $this->supported['CurrId'] = 1;
  100.         $this->supported['SelectRowRanges'] = 1;
  101.         $this->supported['LOBs'] = 1;
  102.         $this->supported['Replace'] = 1;
  103.         $this->supported['SubSelects'] = 1;
  104.         
  105.         $this->decimal_factor = pow(10.0, $this->decimal_places);
  106.     }
  107.  
  108.     // }}}
  109.     // {{{ errorCode()
  110.  
  111.     /**
  112.      * Map native error codes to DB's portable ones.  Requires that
  113.      * the DB implementation's constructor fills in the $errorcode_map
  114.      * property.
  115.      *
  116.      * @param $nativecode the native error code, as returned by the backend
  117.      * database extension (string or integer)
  118.      * @return int a portable MDB error code, or FALSE if this DB
  119.      * implementation has no mapping for the given error code.
  120.      */
  121.     function errorCode($errormsg)
  122.     {
  123.         static $error_regexps;
  124.         if (empty($error_regexps)) {
  125.             $error_regexps = array(
  126.                 '/([Tt]able does not exist\.|[Rr]elation [\"\'].*[\"\'] does not exist|[Ss]equence does not exist|[Cc]lass ".+" not found)$/' => MDB_ERROR_NOSUCHTABLE,
  127.                 '/[Tt]able [\"\'].*[\"\'] does not exist/' => MDB_ERROR_NOSUCHTABLE,
  128.                 '/[Rr]elation [\"\'].*[\"\'] already exists|[Cc]annot insert a duplicate key into (a )?unique index.*/' => MDB_ERROR_ALREADY_EXISTS,
  129.                 '/divide by zero$/'                     => MDB_ERROR_DIVZERO,
  130.                 '/pg_atoi: error in .*: can\'t parse /' => MDB_ERROR_INVALID_NUMBER,
  131.                 '/ttribute [\"\'].*[\"\'] not found$|[Rr]elation [\"\'].*[\"\'] does not have attribute [\"\'].*[\"\']/' => MDB_ERROR_NOSUCHFIELD,
  132.                 '/parser: parse error at or near \"/'   => MDB_ERROR_SYNTAX,
  133.                 '/referential integrity violation/'     => MDB_ERROR_CONSTRAINT
  134.             );
  135.         }
  136.         foreach ($error_regexps as $regexp => $code) {
  137.             if (preg_match($regexp, $errormsg)) {
  138.                 return($code);
  139.             }
  140.         }
  141.         // Fall back to MDB_ERROR if there was no mapping.
  142.         return(MDB_ERROR);
  143.     }
  144.     // }}}
  145.     // {{{ pgsqlRaiseError()
  146.  
  147.     /**
  148.      * This method is used to communicate an error and invoke error
  149.      * callbacks etc.  Basically a wrapper for MDB::raiseError
  150.      * that checks for native error msgs.
  151.      *
  152.      * @param integer $errno error code
  153.      * @return object a PEAR error object
  154.      * @access public
  155.      * @see PEAR_Error
  156.      */
  157.  
  158.     function pgsqlRaiseError($errno = NULL)
  159.     {
  160.         $native = $this->errorNative();
  161.         if ($errno === NULL) {
  162.             $err = $this->errorCode($native);
  163.         } else {
  164.             $err = $errno;
  165.         }
  166.         return($this->raiseError($err, NULL, NULL, NULL, $native));
  167.     }
  168.  
  169.     // }}}
  170.     // {{{ errorNative()
  171.  
  172.     /**
  173.      * Get the native error code of the last error (if any) that
  174.      * occured on the current connection.
  175.      *
  176.      * @access public
  177.      *
  178.      * @return int native pgsql error code
  179.      */
  180.     function errorNative()
  181.     {
  182.         return pg_ErrorMessage($this->connection);
  183.     }
  184.  
  185.  
  186.     // }}}
  187.     // {{{ autoCommit()
  188.  
  189.     /**
  190.      * Define whether database changes done on the database be automatically
  191.      * committed. This function may also implicitly start or end a transaction.
  192.      *
  193.      * @param boolean $auto_commit flag that indicates whether the database
  194.      *     changes should be committed right after executing every query
  195.      *     statement. If this argument is 0 a transaction implicitly started.
  196.      *     Otherwise, if a transaction is in progress it is ended by committing
  197.      *     any database changes that were pending.
  198.      * @return mixed MDB_OK on success, a MDB error on failure
  199.      * @access public
  200.      */
  201.     function autoCommit($auto_commit)
  202.     {
  203.         $this->debug('AutoCommit: '.($auto_commit ? 'On' : 'Off'));
  204.         if ($this->auto_commit == $auto_commit) {
  205.             return(MDB_OK);
  206.         }
  207.         if ($this->connection) {
  208.             if (MDB::isError($result = $this->_doQuery($auto_commit ? 'END' : 'BEGIN')))
  209.                 return($result);
  210.         }
  211.         $this->auto_commit = $auto_commit;
  212.         $this->in_transaction = !$auto_commit;
  213.         return(MDB_OK);
  214.     }
  215.  
  216.     // }}}
  217.     // {{{ commit()
  218.  
  219.     /**
  220.      * Commit the database changes done during a transaction that is in
  221.      * progress. This function may only be called when auto-committing is
  222.      * disabled, otherwise it will fail. Therefore, a new transaction is
  223.      * implicitly started after committing the pending changes.
  224.      *
  225.      * @return mixed MDB_OK on success, a MDB error on failure
  226.      * @access public
  227.      */
  228.     function commit()
  229.     {
  230.          $this->debug('Commit Transaction');
  231.         if ($this->auto_commit) {
  232.             return($this->raiseError(MDB_ERROR, NULL, NULL, 'Commit: transaction changes are being auto commited'));
  233.         }
  234.         return($this->_doQuery('COMMIT') && $this->_doQuery('BEGIN'));
  235.     }
  236.  
  237.     // }}}
  238.     // {{{ rollback()
  239.  
  240.     /**
  241.      * Cancel any database changes done during a transaction that is in
  242.      * progress. This function may only be called when auto-committing is
  243.      * disabled, otherwise it will fail. Therefore, a new transaction is
  244.      * implicitly started after canceling the pending changes.
  245.      *
  246.      * @return mixed MDB_OK on success, a MDB error on failure
  247.      * @access public
  248.      */
  249.     function rollback()
  250.     {
  251.          $this->debug('Rollback Transaction');
  252.         if ($this->auto_commit) {
  253.             return($this->raiseError(MDB_ERROR, NULL, NULL, 'Rollback: transactions can not be rolled back when changes are auto commited'));
  254.         }
  255.         return($this->_doQuery('ROLLBACK') && $this->_doQuery('BEGIN'));
  256.     }
  257.  
  258.     // }}}
  259.     // {{{ _doConnect()
  260.  
  261.     /**
  262.      * Does the grunt work of connecting to the database
  263.      *
  264.      * @return mixed connection resource on success, MDB_Error on failure
  265.      * @access private
  266.      **/
  267.     function _doConnect($database_name, $persistent)
  268.     {
  269.         $function = ($persistent ? 'pg_pconnect' : 'pg_connect');
  270.         if (!function_exists($function)) {
  271.             return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL, 'doConnect: PostgreSQL support is not available in this PHP configuration'));
  272.         }
  273.         $port = (isset($this->port) ? $this->port : '');
  274.         if ($database_name == '') {
  275.             $database_name = 'template1';
  276.         }
  277.         $connect_string = 'dbname='.$database_name;
  278.         if ($this->host != '') {
  279.             $connect_string .= ' host='.$this->host;
  280.         }
  281.         if ($port != '') {
  282.             $connect_string .= ' port='.strval($port);
  283.         }
  284.         if ($this->user != '') {
  285.             $connect_string .= ' user='.$this->user;
  286.         }
  287.         if ($this->password != '') {
  288.             $connect_string .= ' password='.$this->password;
  289.         }
  290.         putenv('PGDATESTYLE=ISO');
  291.         if (($connection = @$function($connect_string)) > 0) {
  292.             return($connection);
  293.         }
  294.         if (isset($php_errormsg)) {
  295.             $error_msg = $php_errormsg;
  296.         } else {
  297.             $error_msg = 'Could not connect to PostgreSQL server';
  298.         }
  299.         return($this->raiseError(MDB_ERROR_CONNECT_FAILED, NULL, NULL, 'doConnect: '.$error_msg));
  300.     }
  301.  
  302.     // }}}
  303.     // {{{ connect()
  304.  
  305.     /**
  306.      * Connect to the database
  307.      *
  308.      * @return TRUE on success, MDB_Error on failure
  309.      * @access public
  310.      **/
  311.     function connect()
  312.     {
  313.         $port = (isset($this->options['port']) ? $this->options['port'] : '');
  314.         if($this->connection != 0) {
  315.             if (!strcmp($this->connected_host, $this->host)
  316.                 && !strcmp($this->connected_port, $port)
  317.                 && !strcmp($this->selected_database, $this->database_name)
  318.                 && ($this->opened_persistent == $this->options['persistent']))
  319.             {
  320.                 return(MDB_OK);
  321.             }
  322.             pg_Close($this->connection);
  323.             $this->affected_rows = -1;
  324.             $this->connection = 0;
  325.         }
  326.  
  327.         if(!PEAR::loadExtension($this->phptype)) {
  328.             return(PEAR::raiseError(NULL, MDB_ERROR_NOT_FOUND,
  329.                 NULL, NULL, 'extension '.$this->phptype.' is not compiled into PHP',
  330.                 'MDB_Error', TRUE));
  331.         }
  332.  
  333.         if(function_exists('pg_cmdTuples')) {
  334.             $connection = $this->_doConnect('template1', 0);
  335.             if (!MDB::isError($connection)) {
  336.                 if (($result = @pg_Exec($connection, 'BEGIN'))) {
  337.                     $error_reporting = error_reporting(63);
  338.                     @pg_cmdTuples($result);
  339.                     if (!isset($php_errormsg) || strcmp($php_errormsg, 'This compilation does not support pg_cmdtuples()')) {
  340.                         $this->supported['AffectedRows'] = 1;
  341.                     }
  342.                     error_reporting($error_reporting);
  343.                 } else {
  344.                     $err = $this->raiseError(MDB_ERROR, NULL, NULL, 'Setup: '.pg_ErrorMessage($connection));
  345.                 }
  346.                 pg_Close($connection);
  347.             } else {
  348.                 $err = $this->raiseError(MDB_ERROR, NULL, NULL, 'Setup: could not execute BEGIN');
  349.             }
  350.             if (isset($err) && MDB::isError($err)) {
  351.                 return($err);
  352.             }
  353.         }
  354.         $connection = $this->_doConnect($this->database_name, $this->options['persistent']);
  355.         if (MDB::isError($connection)) {
  356.             return($connection);
  357.         }
  358.         $this->connection = $connection;
  359.         
  360.         if (!$this->auto_commit && MDB::isError($trans_result = $this->_doQuery('BEGIN'))) {
  361.             pg_Close($this->connection);
  362.             $this->connection = 0;
  363.             $this->affected_rows = -1;
  364.             return($trans_result);
  365.         }
  366.         $this->connected_host = $this->host;
  367.         $this->connected_port = $port;
  368.         $this->selected_database = $this->database_name;
  369.         $this->opened_persistent = $this->options['persistent'];
  370.         return(MDB_OK);
  371.     }
  372.  
  373.     // }}}
  374.     // {{{ _close()
  375.     /**
  376.      * Close the database connection
  377.      *
  378.      * @return boolean
  379.      * @access private
  380.      **/
  381.     function _close()
  382.     {
  383.         if ($this->connection != 0) {
  384.             if (!$this->auto_commit) {
  385.                 $this->_doQuery('END');
  386.             }
  387.             pg_Close($this->connection);
  388.             $this->connection = 0;
  389.             $this->affected_rows = -1;
  390.             
  391.             unset($GLOBALS['_MDB_databases'][$this->database]);
  392.             return(MDB_OK);
  393.         }
  394.         return(MDB_ERROR);
  395.     }
  396.  
  397.     // }}}
  398.     // {{{ _doQuery()
  399.  
  400.     /**
  401.      * Execute a query
  402.      * @param string $query the SQL query
  403.      * @return mixed result identifier if query executed, else MDB_error
  404.      * @access private
  405.      **/
  406.     function _doQuery($query)
  407.     {
  408.         if (($result = @pg_Exec($this->connection, $query))) {
  409.             $this->affected_rows = (isset($this->supported['AffectedRows']) ? pg_cmdTuples($result) : -1);
  410.         } else {
  411.             $error = pg_ErrorMessage($this->connection);
  412.             return($this->pgsqlRaiseError());
  413.         }
  414.         return($result);
  415.     }
  416.  
  417.     // }}}
  418.     // {{{ _standaloneQuery()
  419.  
  420.     /**
  421.      * execute a query
  422.      *
  423.      * @param string $query
  424.      * @return
  425.      * @access private
  426.      */
  427.     function _standaloneQuery($query)
  428.     {
  429.         if (($connection = $this->_doConnect('template1', 0)) == 0) {
  430.             return($this->raiseError(MDB_ERROR_CONNECT_FAILED, NULL, NULL, '_standaloneQuery: Cannot connect to template1'));
  431.         }
  432.         if (!($result = @pg_Exec($connection, $query))) {
  433.             $this->raiseError(MDB_ERROR, NULL, NULL, '_standaloneQuery: ' . pg_ErrorMessage($connection));
  434.         }
  435.         pg_Close($connection);
  436.         return($result);
  437.     }
  438.  
  439.     // }}}
  440.     // {{{ query()
  441.  
  442.     /**
  443.      * Send a query to the database and return any results
  444.      *
  445.      * @param string $query the SQL query
  446.      * @param array $types array that contains the types of the columns in
  447.      *                         the result set
  448.      * @return mixed result identifier if query executed, else MDB_error
  449.      * @access public
  450.      **/
  451.     function query($query, $types = NULL)
  452.     {
  453.         $this->debug("Query: $query");
  454.         $ismanip = MDB::isManip($query);
  455.         $this->last_query = $query;
  456.         $first = $this->first_selected_row;
  457.         $limit = $this->selected_row_limit;
  458.         $this->first_selected_row = $this->selected_row_limit = 0;
  459.         $connected = $this->connect();
  460.         if (MDB::isError($connected)) {
  461.             return($connected);
  462.         }
  463.         
  464.         if (!$ismanip && $limit > 0) {
  465.              if ($this->auto_commit && MDB::isError($this->_doQuery('BEGIN'))) {
  466.                  return($this->raiseError(MDB_ERROR));
  467.              }
  468.              $result = $this->_doQuery('DECLARE select_cursor SCROLL CURSOR FOR '.$query);
  469.              if (!MDB::isError($result)) {
  470.                  if ($first > 0 && MDB::isError($result = $this->_doQuery("MOVE FORWARD $first FROM select_cursor"))) {
  471.                      $this->freeResult($result);
  472.                      return($result);
  473.                  }
  474.                  if (MDB::isError($result = $this->_doQuery("FETCH FORWARD $limit FROM select_cursor"))) {
  475.                      $this->freeResult($result);
  476.                      return($result);
  477.                  }
  478.              } else {
  479.                  return($result);
  480.              }
  481.              if ($this->auto_commit && MDB::isError($result2 = $this->_doQuery('END'))) {
  482.                  $this->freeResult($result);
  483.                  return($result2);
  484.              }
  485.          } else {
  486.             $result = $this->_doQuery($query);
  487.             if (MDB::isError($result)) {
  488.                 return($result);
  489.             }
  490.         }
  491.         if ($ismanip) {
  492.             $this->affected_rows = @pg_cmdtuples($result);
  493.             return(MDB_OK);
  494.         } elseif ((preg_match('/^\s*\(?\s*SELECT\s+/si', $query)
  495.                 && !preg_match('/^\s*\(?\s*SELECT\s+INTO\s/si', $query)
  496.             ) || preg_match('/^\s*EXPLAIN/si',$query )
  497.         ) {
  498.             /* PostgreSQL commands:
  499.                ABORT, ALTER, BEGIN, CLOSE, CLUSTER, COMMIT, COPY,
  500.                CREATE, DECLARE, DELETE, DROP TABLE, EXPLAIN, FETCH,
  501.                GRANT, INSERT, LISTEN, LOAD, LOCK, MOVE, NOTIFY, RESET,
  502.                REVOKE, ROLLBACK, SELECT, SELECT INTO, SET, SHOW,
  503.                UNLISTEN, UPDATE, VACUUM
  504.             */
  505.             $this->highest_fetched_row[$result] = -1;
  506.             if ($types != NULL) {
  507.                 if (!is_array($types)) {
  508.                     $types = array($types);
  509.                 }
  510.                 if (MDB::isError($err = $this->setResultTypes($result, $types))) {
  511.                     $this->freeResult($result);
  512.                     return($err);
  513.                 }
  514.             }
  515.             return($result);
  516.         } else {
  517.             $this->affected_rows = 0;
  518.             return(MDB_OK);
  519.         }
  520.         return($this->raiseError(MDB_ERROR));
  521.     }
  522.  
  523.     // }}}
  524.     // {{{ getColumnNames()
  525.  
  526.     /**
  527.      * Retrieve the names of columns returned by the DBMS in a query result.
  528.      *
  529.      * @param resource $result  result identifier
  530.      * @return mixed associative array variable
  531.      *      that holds the names of columns. The indexes of the array are
  532.      *      the column names mapped to lower case and the values are the
  533.      *      respective numbers of the columns starting from 0. Some DBMS may
  534.      *      not return any columns when the result set does not contain any
  535.      *      rows.
  536.      *     a MDB error on failure
  537.      * @access public
  538.      */
  539.     function getColumnNames($result)
  540.     {
  541.         if (!isset($this->highest_fetched_row[$result])) {
  542.             return($this->raiseError(MDB_ERROR, NULL, NULL, 'Get Column Names: specified an nonexistant result set'));
  543.         }
  544.         if (!isset($this->columns[$result])) {
  545.             $this->columns[$result] = array();
  546.             $columns = pg_numfields($result);
  547.             for($column = 0; $column < $columns; $column++) {
  548.                 $this->columns[$result][strtolower(pg_fieldname($result, $column))] = $column;
  549.             }
  550.         }
  551.         return($this->columns[$result]);
  552.     }
  553.  
  554.     // }}}
  555.     // {{{ numCols()
  556.  
  557.     /**
  558.      * Count the number of columns returned by the DBMS in a query result.
  559.      *
  560.      * @param resource $result result identifier
  561.      * @return mixed integer value with the number of columns, a MDB error
  562.      *      on failure
  563.      * @access public
  564.      */
  565.     function numCols($result)
  566.     {
  567.         if (!isset($this->highest_fetched_row[$result])) {
  568.             return($this->raiseError(MDB_ERROR, NULL, NULL, 'numCols: specified an nonexistant result set'));
  569.         }
  570.         return(pg_numfields($result));
  571.     }
  572.  
  573.     // }}}
  574.     // {{{ endOfResult()
  575.  
  576.     /**
  577.     * check if the end of the result set has been reached
  578.     *
  579.     * @param resource    $result result identifier
  580.     * @return mixed TRUE or FALSE on sucess, a MDB error on failure
  581.     * @access public
  582.     */
  583.     function endOfResult($result)
  584.     {
  585.         if (!isset($this->highest_fetched_row[$result])) {
  586.             return($this->RaiseError(MDB_ERROR, NULL, NULL, 'End of result attempted to check the end of an unknown result'));
  587.         }
  588.         return($this->highest_fetched_row[$result] >= $this->numRows($result) - 1);
  589.     }
  590.  
  591.     // }}}
  592.     // {{{ fetch()
  593.  
  594.     /**
  595.      * fetch value from a result set
  596.      *
  597.      * @param resource $result result identifier
  598.      * @param int $row number of the row where the data can be found
  599.      * @param int $field field number where the data can be found
  600.      * @return mixed string on success, a MDB error on failure
  601.      * @access public
  602.      */
  603.     function fetch($result, $row, $field)
  604.     {
  605.         $this->highest_fetched_row[$result] = max($this->highest_fetched_row[$result], $row);
  606.         $res = @pg_result($result, $row, $field);
  607.         if ($res === FALSE && $res != NULL) {
  608.             return($this->pgsqlRaiseError());
  609.         }
  610.         return($res);
  611.     }
  612.  
  613.     // }}}
  614.     // {{{ _retrieveLob()
  615.  
  616.     /**
  617.      * fetch a float value from a result set
  618.      *
  619.      * @param int $lob handle to a lob created by the createLob() function
  620.      * @return mixed MDB_OK on success, a MDB error on failure
  621.      * @access private
  622.      */
  623.     function _retrieveLob($lob)
  624.     {
  625.         if (!isset($this->lobs[$lob])) {
  626.             return($this->raiseError(MDB_ERROR_INVALID, NULL, NULL, 'Retrieve LOB: did not specified a valid lob'));
  627.         }
  628.         if (!isset($this->lobs[$lob]['Value'])) {
  629.             if ($this->auto_commit) {
  630.                 if (!@pg_exec($this->connection, 'BEGIN')) {
  631.                     return($this->raiseError(MDB_ERROR,  NULL, NULL, 'Retrieve LOB: ' . pg_ErrorMessage($this->connection)));
  632.                 }
  633.                 $this->lobs[$lob]['InTransaction'] = 1;
  634.             }
  635.             $this->lobs[$lob]['Value'] = $this->fetch($this->lobs[$lob]['Result'], $this->lobs[$lob]['Row'], $this->lobs[$lob]['Field']);
  636.             if (!($this->lobs[$lob]['Handle'] = @pg_loopen($this->connection, $this->lobs[$lob]['Value'], 'r'))) {
  637.                 if (isset($this->lobs[$lob]['InTransaction'])) {
  638.                     @pg_Exec($this->connection, 'END');
  639.                     unset($this->lobs[$lob]['InTransaction']);
  640.                 }
  641.                 unset($this->lobs[$lob]['Value']);
  642.                 return($this->raiseError(MDB_ERROR, NULL, NULL, 'Retrieve LOB: ' . pg_ErrorMessage($this->connection)));
  643.             }
  644.         }
  645.         return(MDB_OK);
  646.     }
  647.  
  648.     // }}}
  649.     // {{{ endOfResultLob()
  650.  
  651.     /**
  652.      * Determine whether it was reached the end of the large object and
  653.      * therefore there is no more data to be read for the its input stream.
  654.      *
  655.      * @param int    $lob handle to a lob created by the createLob() function
  656.      * @return mixed TRUE or FALSE on success, a MDB error on failure
  657.      * @access public
  658.      */
  659.     function endOfResultLob($lob)
  660.     {
  661.         $lobresult = $this->_retrieveLob($lob);
  662.         if (MDB::isError($lobresult)) {
  663.             return($lobresult);
  664.         }
  665.         return(isset($this->lobs[$lob]['EndOfLOB']));
  666.     }
  667.  
  668.     // }}}
  669.     // {{{ _readResultLob()
  670.  
  671.     /**
  672.      * Read data from large object input stream.
  673.      *
  674.      * @param int $lob handle to a lob created by the createLob() function
  675.      * @param blob $data reference to a variable that will hold data to be
  676.      *      read from the large object input stream
  677.      * @param int $length integer value that indicates the largest ammount of
  678.      *      data to be read from the large object input stream.
  679.      * @return mixed length on success, a MDB error on failure
  680.      * @access private
  681.      */
  682.     function _readResultLob($lob, &$data, $length)
  683.     {
  684.         $lobresult = $this->_retrieveLob($lob);
  685.         if (MDB::isError($lobresult)) {
  686.             return($lobresult);
  687.         }
  688.         $data = pg_loread($this->lobs[$lob]['Handle'], $length);
  689.         if (gettype($data) != 'string') {
  690.             $this->raiseError(MDB_ERROR, NULL, NULL, 'Read Result LOB: ' . pg_ErrorMessage($this->connection));
  691.         }
  692.         if (($length = strlen($data)) == 0) {
  693.             $this->lobs[$lob]['EndOfLOB'] = 1;
  694.         }
  695.         return($length);
  696.     }
  697.  
  698.     // }}}
  699.     // {{{ _destroyResultLob()
  700.  
  701.     /**
  702.      * Free any resources allocated during the lifetime of the large object
  703.      * handler object.
  704.      *
  705.      * @param int $lob handle to a lob created by the createLob() function
  706.      * @access private
  707.      */
  708.     function _destroyResultLob($lob)
  709.     {
  710.         if (isset($this->lobs[$lob])) {
  711.             if (isset($this->lobs[$lob]['Value'])) {
  712.                 pg_loclose($this->lobs[$lob]['Handle']);
  713.                 if (isset($this->lobs[$lob]['InTransaction'])) {
  714.                     @pg_Exec($this->connection, 'END');
  715.                 }
  716.             }
  717.             $this->lobs[$lob] = '';
  718.         }
  719.     }
  720.  
  721.     // }}}
  722.     // {{{ fetchClob()
  723.  
  724.     /**
  725.      * fetch a clob value from a result set
  726.      *
  727.      * @param resource $result result identifier
  728.      * @param int $row number of the row where the data can be found
  729.      * @param int $field field number where the data can be found
  730.      * @return mixed content of the specified data cell, a MDB error on failure,
  731.      *       a MDB error on failure
  732.      * @access public
  733.      */
  734.     function fetchClob($result, $row, $field)
  735.     {
  736.         return($this->fetchLob($result, $row, $field));
  737.     }
  738.  
  739.     // }}}
  740.     // {{{ fetchBlob()
  741.  
  742.     /**
  743.      * fetch a blob value from a result set
  744.      *
  745.      * @param resource $result result identifier
  746.      * @param int $row number of the row where the data can be found
  747.      * @param int $field field number where the data can be found
  748.      * @return mixed content of the specified data cell, a MDB error on failure
  749.      * @access public
  750.      */
  751.     function fetchBlob($result, $row, $field)
  752.     {
  753.         return($this->fetchLob($result, $row, $field));
  754.     }
  755.  
  756.     // }}}
  757.     // {{{ convertResult()
  758.  
  759.     /**
  760.      * convert a value to a RDBMS indepdenant MDB type
  761.      *
  762.      * @param mixed $value value to be converted
  763.      * @param int $type constant that specifies which type to convert to
  764.      * @return mixed converted value or a MDB error on failure
  765.      * @access public
  766.      */
  767.     function convertResult($value, $type)
  768.     {
  769.         switch ($type) {
  770.             case MDB_TYPE_DECIMAL:
  771.                 return(sprintf('%.'.$this->decimal_places.'f',doubleval($value)/$this->decimal_factor));
  772.             case MDB_TYPE_TIMESTAMP:
  773.                 return substr($value, 0, strlen('YYYY-MM-DD HH:MM:SS'));
  774.             default:
  775.                 return($this->_baseConvertResult($value, $type));
  776.         }
  777.     }
  778.  
  779.     // }}}
  780.     // {{{ resultIsNull()
  781.  
  782.     /**
  783.      * Determine whether the value of a query result located in given row and
  784.      *   field is a NULL.
  785.      *
  786.      * @param resource    $result result identifier
  787.      * @param int    $row    number of the row where the data can be found
  788.      * @param int    $field    field number where the data can be found
  789.      * @return mixed TRUE or FALSE on success, a MDB error on failure
  790.      * @access public
  791.      */
  792.     function resultIsNull($result, $row, $field)
  793.     {
  794.         $this->highest_fetched_row[$result] = max($this->highest_fetched_row[$result], $row);
  795.         return(@pg_FieldIsNull($result, $row, $field));
  796.     }
  797.  
  798.     // }}}
  799.     // {{{ numRows()
  800.  
  801.     /**
  802.      * returns the number of rows in a result object
  803.      *
  804.      * @param ressource $result a valid result ressouce pointer
  805.      * @return mixed MDB_Error or the number of rows
  806.      * @access public
  807.      */
  808.     function numRows($result)
  809.     {
  810.         return(pg_numrows($result));
  811.     }
  812.  
  813.     // }}}
  814.     // {{{ freeResult()
  815.  
  816.     /**
  817.      * Free the internal resources associated with $result.
  818.      *
  819.      * @param $result result identifier
  820.      * @return boolean TRUE on success, FALSE if $result is invalid
  821.      * @access public
  822.      */
  823.     function freeResult($result)
  824.     {
  825.         if(isset($this->highest_fetched_row[$result])) {
  826.             unset($this->highest_fetched_row[$result]);
  827.         }
  828.         if(isset($this->columns[$result])) {
  829.             unset($this->columns[$result]);
  830.         }
  831.         if(isset($this->result_types[$result])) {
  832.             unset($this->result_types[$result]);
  833.         }
  834.         return(pg_freeresult($result));
  835.     }
  836.  
  837.     // }}}
  838.     // {{{ getTextDeclaration()
  839.  
  840.     /**
  841.      * Obtain DBMS specific SQL code portion needed to declare an text type
  842.      * field to be used in statements like CREATE TABLE.
  843.      *
  844.      * @param string $name   name the field to be declared.
  845.      * @param string $field  associative array with the name of the properties
  846.      *      of the field being declared as array indexes. Currently, the types
  847.      *      of supported field properties are as follows:
  848.      *
  849.      *      length
  850.      *          Integer value that determines the maximum length of the text
  851.      *          field. If this argument is missing the field should be
  852.      *          declared to have the longest length allowed by the DBMS.
  853.      *
  854.      *      default
  855.      *          Text value to be used as default for this field.
  856.      *
  857.      *      notnull
  858.      *          Boolean flag that indicates whether this field is constrained
  859.      *          to not be set to NULL.
  860.      * @return string  DBMS specific SQL code portion that should be used to
  861.      *      declare the specified field.
  862.      * @access public
  863.      */
  864.     function getTextDeclaration($name, $field)
  865.     {
  866.         return((isset($field['length']) ? "$name VARCHAR (" . $field['length'] . ')' : "$name TEXT") . (isset($field['default']) ? " DEFAULT '" . $field['default'] . "'" : '') . (isset($field['notnull']) ? ' NOT NULL' : ''));
  867.     }
  868.  
  869.     // }}}
  870.     // {{{ getClobDeclaration()
  871.  
  872.     /**
  873.      * Obtain DBMS specific SQL code portion needed to declare an character
  874.      * large object type field to be used in statements like CREATE TABLE.
  875.      *
  876.      * @param string $name   name the field to be declared.
  877.      * @param string $field  associative array with the name of the properties
  878.      *      of the field being declared as array indexes. Currently, the types
  879.      *      of supported field properties are as follows:
  880.      *
  881.      *      length
  882.      *          Integer value that determines the maximum length of the large
  883.      *          object field. If this argument is missing the field should be
  884.      *          declared to have the longest length allowed by the DBMS.
  885.      *
  886.      *      notnull
  887.      *          Boolean flag that indicates whether this field is constrained
  888.      *          to not be set to NULL.
  889.      * @return string  DBMS specific SQL code portion that should be used to
  890.      *      declare the specified field.
  891.      * @access public
  892.      */
  893.     function getClobDeclaration($name, $field)
  894.     {
  895.         return("$name OID".(isset($field['notnull']) ? ' NOT NULL' : ''));
  896.     }
  897.  
  898.     // }}}
  899.     // {{{ getBlobDeclaration()
  900.  
  901.     /**
  902.      * Obtain DBMS specific SQL code portion needed to declare an binary large
  903.      * object type field to be used in statements like CREATE TABLE.
  904.      *
  905.      * @param string $name   name the field to be declared.
  906.      * @param string $field  associative array with the name of the properties
  907.      *      of the field being declared as array indexes. Currently, the types
  908.      *      of supported field properties are as follows:
  909.      *
  910.      *      length
  911.      *          Integer value that determines the maximum length of the large
  912.      *          object field. If this argument is missing the field should be
  913.      *          declared to have the longest length allowed by the DBMS.
  914.      *
  915.      *      notnull
  916.      *          Boolean flag that indicates whether this field is constrained
  917.      *          to not be set to NULL.
  918.      * @return string  DBMS specific SQL code portion that should be used to
  919.      *      declare the specified field.
  920.      * @access public
  921.      */
  922.     function getBlobDeclaration($name, $field)
  923.     {
  924.         return("$name OID".(isset($field['notnull']) ? ' NOT NULL' : ''));
  925.     }
  926.  
  927.     // }}}
  928.     // {{{ getDateDeclaration()
  929.  
  930.     /**
  931.      * Obtain DBMS specific SQL code portion needed to declare a date type
  932.      * field to be used in statements like CREATE TABLE.
  933.      *
  934.      * @param string $name   name the field to be declared.
  935.      * @param string $field  associative array with the name of the properties
  936.      *      of the field being declared as array indexes. Currently, the types
  937.      *      of supported field properties are as follows:
  938.      *
  939.      *      default
  940.      *          Date value to be used as default for this field.
  941.      *
  942.      *      notnull
  943.      *          Boolean flag that indicates whether this field is constrained
  944.      *          to not be set to NULL.
  945.      * @return string  DBMS specific SQL code portion that should be used to
  946.      *      declare the specified field.
  947.      * @access public
  948.      */
  949.     function getDateDeclaration($name, $field)
  950.     {
  951.         return($name.' DATE'.(isset($field['default']) ? ' DEFAULT \''.$field['default'] . "'" : '').(isset($field['notnull']) ? ' NOT NULL' : ''));
  952.     }
  953.  
  954.     // }}}
  955.     // {{{ getTimeDeclaration()
  956.  
  957.     /**
  958.      * Obtain DBMS specific SQL code portion needed to declare a time
  959.      * field to be used in statements like CREATE TABLE.
  960.      *
  961.      * @param string $name   name the field to be declared.
  962.      * @param string $field  associative array with the name of the properties
  963.      *      of the field being declared as array indexes. Currently, the types
  964.      *      of supported field properties are as follows:
  965.      *
  966.      *      default
  967.      *          Time value to be used as default for this field.
  968.      *
  969.      *      notnull
  970.      *          Boolean flag that indicates whether this field is constrained
  971.      *          to not be set to NULL.
  972.      * @return string  DBMS specific SQL code portion that should be used to
  973.      *      declare the specified field.
  974.      * @access public
  975.      */
  976.     function getTimeDeclaration($name, $field)
  977.     {
  978.         return($name.' TIME'.(isset($field['default']) ? ' DEFAULT \''.$field['default'].'\'' : '').(isset($field['notnull']) ? ' NOT NULL' : ''));
  979.     }
  980.  
  981.     // }}}
  982.     // {{{ getFloatDeclaration()
  983.  
  984.     /**
  985.      * Obtain DBMS specific SQL code portion needed to declare a float type
  986.      * field to be used in statements like CREATE TABLE.
  987.      *
  988.      * @param string $name   name the field to be declared.
  989.      * @param string $field  associative array with the name of the properties
  990.      *      of the field being declared as array indexes. Currently, the types
  991.      *      of supported field properties are as follows:
  992.      *
  993.      *      default
  994.      *          Float value to be used as default for this field.
  995.      *
  996.      *      notnull
  997.      *          Boolean flag that indicates whether this field is constrained
  998.      *          to not be set to NULL.
  999.      * @return string  DBMS specific SQL code portion that should be used to
  1000.      *      declare the specified field.
  1001.      * @access public
  1002.      */
  1003.     function getFloatDeclaration($name, $field)
  1004.     {
  1005.         return("$name FLOAT8 ".(isset($field['default']) ? ' DEFAULT '.$this->getFloatValue($field['default']) : '').(isset($field['notnull']) ? ' NOT NULL' : ''));
  1006.     }
  1007.  
  1008.     // }}}
  1009.     // {{{ getDecimalDeclaration()
  1010.  
  1011.     /**
  1012.      * Obtain DBMS specific SQL code portion needed to declare a decimal type
  1013.      * field to be used in statements like CREATE TABLE.
  1014.      *
  1015.      * @param string $name   name the field to be declared.
  1016.      * @param string $field  associative array with the name of the properties
  1017.      *      of the field being declared as array indexes. Currently, the types
  1018.      *      of supported field properties are as follows:
  1019.      *
  1020.      *      default
  1021.      *          Decimal value to be used as default for this field.
  1022.      *
  1023.      *      notnull
  1024.      *          Boolean flag that indicates whether this field is constrained
  1025.      *          to not be set to NULL.
  1026.      * @return string  DBMS specific SQL code portion that should be used to
  1027.      *      declare the specified field.
  1028.      * @access public
  1029.      */
  1030.     function getDecimalDeclaration($name, $field)
  1031.     {
  1032.         return("$name INT8 ".(isset($field['default']) ? ' DEFAULT '.$this->getDecimalValue($field['default']) : '').(isset($field['notnull']) ? ' NOT NULL' : ''));
  1033.     }
  1034.  
  1035.     // }}}
  1036.     // {{{ _getLobValue()
  1037.  
  1038.     /**
  1039.      * Convert a text value into a DBMS specific format that is suitable to
  1040.      * compose query statements.
  1041.      *
  1042.      * @param resource  $prepared_query query handle from prepare()
  1043.      * @param           $parameter
  1044.      * @param           $lob
  1045.      * @return string text string that represents the given argument value in
  1046.      *      a DBMS specific format.
  1047.      * @access private
  1048.      */
  1049.     function _getLobValue($prepared_query, $parameter, $lob)
  1050.     {
  1051.         $connect = $this->connect();
  1052.         if (MDB::isError($connect)) {
  1053.             return($connect);
  1054.         }
  1055.         if ($this->auto_commit && !@pg_Exec($this->connection, 'BEGIN')) {
  1056.             return($this->raiseError(MDB_ERROR, NULL, NULL, '_getLobValue: error starting transaction'));
  1057.         }
  1058.         if (($lo = pg_locreate($this->connection))) {
  1059.             if (($handle = pg_loopen($this->connection, $lo, 'w'))) {
  1060.                 while (!$this->endOfLob($lob)) {
  1061.                     if (MDB::isError($result = $this->readLob($lob, $data, $this->options['lob_buffer_length']))) {
  1062.                         break;
  1063.                     }
  1064.                     if (!pg_lowrite($handle, $data)) {
  1065.                         $result = $this->raiseError(MDB_ERROR, NULL, NULL, 'Get LOB field value: ' . pg_ErrorMessage($this->connection));
  1066.                         break;
  1067.                     }
  1068.                 }
  1069.                 pg_loclose($handle);
  1070.                 if (!MDB::isError($result)) {
  1071.                     $value = strval($lo);
  1072.                 }
  1073.             } else {
  1074.                 $result = $this->raiseError(MDB_ERROR, NULL, NULL, 'Get LOB field value: ' .  pg_ErrorMessage($this->connection));
  1075.             }
  1076.             if (MDB::isError($result)) {
  1077.                 $result = pg_lounlink($this->connection, $lo);
  1078.             }
  1079.         } else {
  1080.             $result = $this->raiseError(MDB_ERROR, NULL, NULL, 'Get LOB field value: ' . pg_ErrorMessage($this->connection));
  1081.         }
  1082.         if ($this->auto_commit) {
  1083.             @pg_Exec($this->connection, 'END');
  1084.         }
  1085.         if (MDB::isError($result)) {
  1086.             return($result);
  1087.         }
  1088.         return($value);
  1089.     }
  1090.  
  1091.     // }}}
  1092.     // {{{ getClobValue()
  1093.  
  1094.     /**
  1095.      * Convert a text value into a DBMS specific format that is suitable to
  1096.      * compose query statements.
  1097.      *
  1098.      * @param resource  $prepared_query query handle from prepare()
  1099.      * @param           $parameter
  1100.      * @param           $clob
  1101.      * @return string text string that represents the given argument value in
  1102.      *      a DBMS specific format.
  1103.      * @access public
  1104.      */
  1105.     function getClobValue($prepared_query, $parameter, $clob)
  1106.     {
  1107.         return($this->_getLobValue($prepared_query, $parameter, $clob));
  1108.     }
  1109.  
  1110.     // }}}
  1111.     // {{{ getBlobValue()
  1112.  
  1113.     /**
  1114.      * Convert a text value into a DBMS specific format that is suitable to
  1115.      * compose query statements.
  1116.      *
  1117.      * @param resource  $prepared_query query handle from prepare()
  1118.      * @param           $parameter
  1119.      * @param           $blob
  1120.      * @return string text string that represents the given argument value in
  1121.      *      a DBMS specific format.
  1122.      * @access public
  1123.      */
  1124.     function getBlobValue($prepared_query, $parameter, $blob)
  1125.     {
  1126.         return($this->_getLobValue($prepared_query, $parameter, $blob));
  1127.     }
  1128.  
  1129.     // }}}
  1130.     // {{{ getFloatValue()
  1131.  
  1132.     /**
  1133.      * Convert a text value into a DBMS specific format that is suitable to
  1134.      * compose query statements.
  1135.      *
  1136.      * @param string $value text string value that is intended to be converted.
  1137.      * @return string text string that represents the given argument value in
  1138.      *      a DBMS specific format.
  1139.      * @access public
  1140.      */
  1141.     function getFloatValue($value)
  1142.     {
  1143.         return(($value === NULL) ? 'NULL' : $value);
  1144.     }
  1145.  
  1146.     // }}}
  1147.     // {{{ getDecimalValue()
  1148.  
  1149.     /**
  1150.      * Convert a text value into a DBMS specific format that is suitable to
  1151.      * compose query statements.
  1152.      *
  1153.      * @param string $value text string value that is intended to be converted.
  1154.      * @return string text string that represents the given argument value in
  1155.      *      a DBMS specific format.
  1156.      * @access public
  1157.      */
  1158.     function getDecimalValue($value)
  1159.     {
  1160.         return(($value === NULL) ? 'NULL' : strval(round($value*$this->decimal_factor)));
  1161.     }
  1162.  
  1163.     // }}}
  1164.     // {{{ nextId()
  1165.  
  1166.     /**
  1167.      * returns the next free id of a sequence
  1168.      *
  1169.      * @param string  $seq_name name of the sequence
  1170.      * @param boolean $ondemand when TRUE the seqence is
  1171.      *                          automatic created, if it
  1172.      *                          not exists
  1173.      * @return mixed MDB_Error or id
  1174.      * @access public
  1175.      */
  1176.     function nextId($seq_name, $ondemand = TRUE)
  1177.     {
  1178.         $seqname = $this->getSequenceName($seq_name);
  1179.         $repeat = 0;
  1180.         do {
  1181.             $this->pushErrorHandling(PEAR_ERROR_RETURN);
  1182.             $result = $this->query("SELECT NEXTVAL('$seqname')");
  1183.             $this->popErrorHandling();
  1184.             if ($ondemand && MDB::isError($result) && $result->getCode() == MDB_ERROR_NOSUCHTABLE) {
  1185.                 $repeat = 1;
  1186.                 $result = $this->createSequence($seq_name);
  1187.                 if (MDB::isError($result)) {
  1188.                     return($this->pgsqlRaiseError($result));
  1189.                 }
  1190.             } else {
  1191.                 $repeat = 0;
  1192.             }
  1193.         } while ($repeat);
  1194.         if (MDB::isError($result)) {
  1195.             return($this->raiseError($result));
  1196.         }
  1197.         return($this->fetchOne($result));
  1198.     }
  1199.  
  1200.     // }}}
  1201.     // {{{ currId()
  1202.  
  1203.     /**
  1204.      * returns the current id of a sequence
  1205.      *
  1206.      * @param string  $seq_name name of the sequence
  1207.      * @return mixed MDB_Error or id
  1208.      * @access public
  1209.      */
  1210.     function currId($seq_name)
  1211.     {
  1212.         $seqname = $this->getSequenceName($seq_name);
  1213.         if (MDB::isError($result = $this->queryOne("SELECT last_value FROM $seqname"))) {
  1214.             return($this->raiseError(MDB_ERROR, NULL, NULL, 'currId: Unable to select from ' . $seqname) );
  1215.         }
  1216.         if (!is_numeric($result)) {
  1217.             return($this->raiseError(MDB_ERROR, NULL, NULL, 'currId: could not find value in sequence table'));
  1218.         }
  1219.         return($result);
  1220.     }
  1221.  
  1222.     // }}}
  1223.     // {{{ fetchInto()
  1224.  
  1225.     /**
  1226.      * Fetch a row and return data in an array.
  1227.      *
  1228.      * @param resource $result result identifier
  1229.      * @param int $fetchmode ignored
  1230.      * @param int $rownum the row number to fetch
  1231.      * @return mixed data array or NULL on success, a MDB error on failure
  1232.      * @access public
  1233.      */
  1234.     function fetchInto($result, $fetchmode = MDB_FETCHMODE_DEFAULT, $rownum = NULL)
  1235.     {
  1236.         if ($fetchmode == MDB_FETCHMODE_DEFAULT) {
  1237.             $fetchmode = $this->fetchmode;
  1238.         }
  1239.         if ($rownum == NULL) {
  1240.             ++$this->highest_fetched_row[$result];
  1241.             $rownum = $this->highest_fetched_row[$result];
  1242.             if ($fetchmode & MDB_FETCHMODE_ASSOC) {
  1243.                 $row = @pg_fetch_assoc($result);
  1244.                 if (is_array($row) && $this->options['optimize'] == 'portability') {
  1245.                     $row = array_change_key_case($row, CASE_LOWER);
  1246.                 }
  1247.             } else {
  1248.                 $row = @pg_fetch_row($result);
  1249.             }
  1250.         } else {
  1251.             $this->highest_fetched_row[$result] = max($this->highest_fetched_row[$result], $rownum);
  1252.             if ($fetchmode & MDB_FETCHMODE_ASSOC) {
  1253.                 $row = @pg_fetch_assoc($result, $rownum);
  1254.                 if (is_array($row) && $this->options['optimize'] == 'portability') {
  1255.                     $row = array_change_key_case($row, CASE_LOWER);
  1256.                 }
  1257.             } else {
  1258.                 $row = @pg_fetch_row($result, $rownum);
  1259.             }
  1260.         }
  1261.         if (!$row) {
  1262.             if ($this->options['autofree']) {
  1263.                 $this->freeResult($result);
  1264.             }
  1265.             return(NULL);
  1266.         }
  1267.         if (isset($this->result_types[$result])) {
  1268.             $row = $this->convertResultRow($result, $row);
  1269.         }
  1270.         return($row);
  1271.     }
  1272.  
  1273.     // }}}
  1274.     // {{{ nextResult()
  1275.  
  1276.     /**
  1277.      * Move the internal pgsql result pointer to the next available result
  1278.      *
  1279.      * @param a valid fbsql result resource
  1280.      * @return true if a result is available otherwise return false
  1281.      * @access public
  1282.      */
  1283.     function nextResult($result)
  1284.     {
  1285.         return(FALSE);
  1286.     }
  1287.  
  1288.     // }}}
  1289.     // {{{ tableInfo()
  1290.  
  1291.     /**
  1292.      * returns meta data about the result set
  1293.      *
  1294.      * @param  mixed $resource PostgreSQL result identifier or table name
  1295.      * @param mixed $mode depends on implementation
  1296.      * @return array an nested array, or a MDB error
  1297.      * @access public
  1298.      */
  1299.     function tableInfo($result, $mode = NULL)
  1300.     {
  1301.         $count = 0;
  1302.         $id = 0;
  1303.         $res = array();
  1304.         
  1305.         /**
  1306.          * depending on $mode, metadata returns the following values:
  1307.          *
  1308.          * - mode is FALSE (default):
  1309.          * $result[]:
  1310.          *    [0]['table']  table name
  1311.          *    [0]['name']   field name
  1312.          *    [0]['type']   field type
  1313.          *    [0]['len']    field length
  1314.          *    [0]['flags']  field flags
  1315.          *
  1316.          * - mode is MDB_TABLEINFO_ORDER
  1317.          * $result[]:
  1318.          *    ['num_fields'] number of metadata records
  1319.          *    [0]['table']  table name
  1320.          *    [0]['name']   field name
  1321.          *    [0]['type']   field type
  1322.          *    [0]['len']    field length
  1323.          *    [0]['flags']  field flags
  1324.          *    ['order'][field name]  index of field named 'field name'
  1325.          *    The last one is used, if you have a field name, but no index.
  1326.          *    Test:  if (isset($result['meta']['myfield'])) { ...
  1327.          *
  1328.          * - mode is MDB_TABLEINFO_ORDERTABLE
  1329.          *     the same as above. but additionally
  1330.          *    ['ordertable'][table name][field name] index of field
  1331.          *       named 'field name'
  1332.          *
  1333.          *       this is, because if you have fields from different
  1334.          *       tables with the same field name * they override each
  1335.          *       other with MDB_TABLEINFO_ORDER
  1336.          *
  1337.          *       you can combine MDB_TABLEINFO_ORDER and
  1338.          *       MDB_TABLEINFO_ORDERTABLE with MDB_TABLEINFO_ORDER |
  1339.          *       MDB_TABLEINFO_ORDERTABLE * or with MDB_TABLEINFO_FULL
  1340.          **/
  1341.         
  1342.         // if $result is a string, then we want information about a
  1343.         // table without a resultset
  1344.         if (is_string($result)) {
  1345.             $id = pg_exec($this->connection, "SELECT * FROM $result LIMIT 0");
  1346.             if (empty($id)) {
  1347.                 return($this->pgsqlRaiseError());
  1348.             }
  1349.         } else { // else we want information about a resultset
  1350.             $id = $result;
  1351.             if (empty($id)) {
  1352.                 return($this->pgsqlRaiseError());
  1353.             }
  1354.         }
  1355.         
  1356.         $count = @pg_numfields($id);
  1357.         
  1358.         // made this IF due to performance (one if is faster than $count if's)
  1359.         if (empty($mode)) {
  1360.             for ($i = 0; $i < $count; $i++) {
  1361.                 $res[$i]['table'] = (is_string($result)) ? $result : '';
  1362.                 $res[$i]['name'] = @pg_fieldname ($id, $i);
  1363.                 $res[$i]['type'] = @pg_fieldtype ($id, $i);
  1364.                 $res[$i]['len'] = @pg_fieldsize ($id, $i);
  1365.                 $res[$i]['flags'] = (is_string($result)) ? $this->_pgFieldflags($id, $i, $result) : '';
  1366.             }
  1367.         } else { // full
  1368.             $res['num_fields'] = $count;
  1369.             
  1370.             for ($i = 0; $i < $count; $i++) {
  1371.                 $res[$i]['table'] = (is_string($result)) ? $result : '';
  1372.                 $res[$i]['name'] = @pg_fieldname ($id, $i);
  1373.                 $res[$i]['type'] = @pg_fieldtype ($id, $i);
  1374.                 $res[$i]['len'] = @pg_fieldsize ($id, $i);
  1375.                 $res[$i]['flags'] = (is_string($result)) ? $this->_pgFieldFlags($id, $i, $result) : '';
  1376.                 if ($mode & MDB_TABLEINFO_ORDER) {
  1377.                     $res['order'][$res[$i]['name']] = $i;
  1378.                 }
  1379.                 if ($mode & MDB_TABLEINFO_ORDERTABLE) {
  1380.                     $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
  1381.                 }
  1382.             }
  1383.         }
  1384.         
  1385.         // free the result only if we were called on a table
  1386.         if (is_string($result) && is_resource($id)) {
  1387.             @pg_freeresult($id);
  1388.         }
  1389.         return($res);
  1390.     }
  1391.  
  1392.     // }}}
  1393.     // {{{ _pgFieldFlags()
  1394.  
  1395.     /**
  1396.      * Flags of a Field
  1397.      *
  1398.      * @param int $resource PostgreSQL result identifier
  1399.      * @param int $num_field the field number
  1400.      * @return string The flags of the field ('not_null', 'default_xx', 'primary_key',
  1401.      *                 'unique' and 'multiple_key' are supported)
  1402.      * @access private
  1403.      **/
  1404.     function _pgFieldFlags($resource, $num_field, $table_name)
  1405.     {
  1406.         $field_name = @pg_fieldname($resource, $num_field);
  1407.         
  1408.         $result = pg_exec($this->connection, "SELECT f.attnotnull, f.atthasdef
  1409.             FROM pg_attribute f, pg_class tab, pg_type typ
  1410.             WHERE tab.relname = typ.typname
  1411.             AND typ.typrelid = f.attrelid
  1412.             AND f.attname = '$field_name'
  1413.             AND tab.relname = '$table_name'");
  1414.         if (pg_numrows($result) > 0) {
  1415.             $row = pg_fetch_row($result, 0);
  1416.             $flags = ($row[0] == 't') ? 'not_null ' : '';
  1417.             
  1418.             if ($row[1] == 't') {
  1419.                 $result = pg_exec($this->connection, "SELECT a.adsrc
  1420.                     FROM pg_attribute f, pg_class tab, pg_type typ, pg_attrdef a
  1421.                     WHERE tab.relname = typ.typname AND typ.typrelid = f.attrelid
  1422.                     AND f.attrelid = a.adrelid AND f.attname = '$field_name'
  1423.                     AND tab.relname = '$table_name'");
  1424.                 $row = pg_fetch_row($result, 0);
  1425.                 $num = str_replace('\'', '', $row[0]);
  1426.                 
  1427.                 $flags .= "default_$num ";
  1428.             }
  1429.         }
  1430.         $result = pg_exec($this->connection, "SELECT i.indisunique, i.indisprimary, i.indkey
  1431.             FROM pg_attribute f, pg_class tab, pg_type typ, pg_index i
  1432.             WHERE tab.relname = typ.typname
  1433.             AND typ.typrelid = f.attrelid
  1434.             AND f.attrelid = i.indrelid
  1435.             AND f.attname = '$field_name'
  1436.             AND tab.relname = '$table_name'");
  1437.         $count = pg_numrows($result);
  1438.         
  1439.         for ($i = 0; $i < $count ; $i++) {
  1440.             $row = pg_fetch_row($result, $i);
  1441.             $keys = explode(' ', $row[2]);
  1442.             
  1443.             if (in_array($num_field + 1, $keys)) {
  1444.                 $flags .= ($row[0] == 't') ? 'unique ' : '';
  1445.                 $flags .= ($row[1] == 't') ? 'primary ' : '';
  1446.                 if (count($keys) > 1)
  1447.                     $flags .= 'multiple_key ';
  1448.             }
  1449.         }
  1450.         
  1451.         return trim($flags);
  1452.     }
  1453. }
  1454.  
  1455. ?>